/*
 *  customer.c
 *  
 *
 *  Created by Rayvionne French on 7/22/11.
 *  Copyright 2011 __MyCompanyName__. All rights reserved.
 *
 */
#include "syscall.h"

#define MAXCLERKS 5
#define MAXSTOCKBOYS 10
#define MAXAISLES 5
#define MAXSHELVES 10
#define MAXSHELFQUANTITY 3
#define MAXCARTSIZE 10
#define SHELVESPERAISLE 2

int globalLock;

/* Customer data */
int customerCount;
int clerkCount;
int cashRegisterCount;
int stockboyCount;

/* To keep track of total customers served */
int totalCustomersServed;
/* The actual store total, not the manager's estimate */
int storeTotal;

/* Home depot's lock for each shelf */
int shelfLocks[MAXSHELVES];
/* Home depot's Shelves */

int shelfState;
int shelfAisle;
int shelfItem;
int shelfQuantity;

/* Represents the line length for each clerk (each index) */
int clerkLineLengths;
/* Clerk line lock ensures that only one instance can check/increment/decrement the line count at a time.*/
int clerkLineLock;
/* For customers waiting in lines */
int clerkLineCV[MAXCLERKS];

/* Lock that protects the checkoutAssistance button */
int checkoutAssistanceLock;
/* Button that is pushed when a customer sees that all clerks are ONBREAK.
 * 1 is need help, 0 if no help needed */
int checkoutAssistance;
/* ConditionVar that will wake up customers when the checkout assistance request
 * has been fulfilled */
int checkoutAssistanceCV;

/* The customer must have a way to know whether the clerk
 * is on break; 0 is working, 1 is on break.*/
int clerkStates;
int clerkLocks[MAXCLERKS];
int clerkCV[MAXCLERKS];

int clerkAssistanceButton;
int clerkAssistanceButtonLock;

/* Lock for knowing which customer are currently being served - by clerk */
int customersAtCheckoutLocks[MAXCLERKS];
int customersAtCheckout;

int refundingTransaction;
int paymentDue;
int amountPaid;
int lastItemGiven;
int itemToScan;

int cashRegisterFinishedStatus; 
int cashRegisterSales;
int cashRegisterAmount;
int cashRegisterLocks[MAXCLERKS];
int cashRegisterCV[MAXCLERKS];

int cashRegisterAssistance;
int cashRegisterAssistanceCV[MAXCLERKS];
int cashRegisterAssistanceLock;
int cashRegisterRequest;

/* Lock for changing aisle location of a stockboy */
int stockboyStatusLock;
/* for current aisle location, -1 means currently not in any aisle */
int stockboyStatus; 

/* Lock for the stockroom */
int stockroomLineLock;
int stockroomLineCV;
int stockroomLineLength;

int stockroomLock;
int stockboyInStockroom;

/* Lock for stockboys */
int stockboyLocks[MAXSTOCKBOYS];
/* State of a stockboy: 0 is working, 1 is on break */
int stockboyStates;
/* Condition variable for a stockboy */
int stockboysCV[MAXSTOCKBOYS];

int stockboyReadyLock[MAXAISLES];
int aisleHelpLine;
int aisleHelpLocks[MAXAISLES];
int aisleCV[MAXAISLES];

int customerWithStockboyLock[MAXSTOCKBOYS];
int customerWithStockboy;
int stockboyItemToHelp;
int shelfNumberForCustomer;
int aisleNumberForCustomer;

/* Lock for customer & manager to access this button */
int customerAssistanceButtonLock;
/* Customer presses on this button if no stockboys can be found */
int customerAssistanceButton;
/* CV for customers to wait on when the manager wakes up all stockboys */
int customerAssistanceButtonCV;
 
 
 
/* Takes in a char array and returns the length of the char array. */
int charCount(char* ch)
{	
	char toString[3];
	int count = 0;
	int i=0;
	while(ch[i] != '\n')
	{
		count++;
		i++;
	}
	return count+1;
}

void CreateMonitorsLocksCVs() {
	
	/* Lock for all global data */
	globalLock = CreateLock("globalLock", 10);
	
	/* Trackers for the number of each entity in simulation */
	customerCount = CreateIntMV("customerCount MV" , charCount("customerCount MV\n")-1, 1);
	clerkCount = CreateIntMV("clerkCount MV" , charCount("clerkCount MV\n")-1, 1);
	cashRegisterCount = CreateIntMV("cashRegisterCount MV" , charCount("cashRegisterCount MV\n")-1, 1);
	stockboyCount = CreateIntMV("stockboyCount MV" , charCount("stockboyCount MV\n")-1, 1);

	/* To keep track of total customers served */
	totalCustomersServed = CreateIntMV("totalCstmrsServed MV" , charCount("totalCstmrsServed MV\n")-1, 1);
	/* The actual store total, not the manager's estimate */
	storeTotal = CreateIntMV("storeTotal MV" , charCount("storeTotal MV\n")-1, 1);
	
	checkoutAssistance = CreateIntMV("checkoutHelp MV" , charCount("checkoutHelp MV\n")-1, 1);
	checkoutAssistanceLock = CreateLock("checkoutHelpLock", charCount("checkoutHelpLock\n")-1);
	checkoutAssistanceCV = CreateCV("checkoutHelpCV", charCount("checkoutHelpCV\n")-1);
	
	/* CLERK LINE MONITOR */
	clerkLineLengths = CreateIntMV("clerkLineLengths MV" , charCount("clerkLineLengths MV\n")-1, MAXCLERKS);
	clerkLineLock = CreateLock("clerkLineLock", 13);
	clerkLineCV[0] = CreateCV("clerkLineCV0", 12);
	clerkLineCV[1] = CreateCV("clerkLineCV1", 12);
	clerkLineCV[2] = CreateCV("clerkLineCV2", 12);
	clerkLineCV[3] = CreateCV("clerkLineCV3", 12);
	clerkLineCV[4] = CreateCV("clerkLineCV4", 12);
	
	/* CLERK MONITOR */
	clerkStates = CreateIntMV("clerkStates MV" , charCount("clerkStates MV\n")-1, MAXCLERKS);
	clerkLocks[0] = CreateLock("clerkLock0", 10);
	clerkLocks[1] = CreateLock("clerkLock1", 10);
	clerkLocks[2] = CreateLock("clerkLock2", 10);
	clerkLocks[3] = CreateLock("clerkLock3", 10);
	clerkLocks[4] = CreateLock("clerkLock4", 10);
	clerkCV[0] = CreateCV("clerkCV0", 8); 
	clerkCV[1] = CreateCV("clerkCV1", 8);
	clerkCV[2] = CreateCV("clerkCV2", 8);
	clerkCV[3] = CreateCV("clerkCV3", 8);
	clerkCV[4] = CreateCV("clerkCV4", 8);

	/* CLERK ASSISTANCE MONITOR */
	clerkAssistanceButton = CreateIntMV("clerkHelpBtn MV" , charCount("clerkHelpBtn MV\n")-1, 1);
	clerkAssistanceButtonLock = CreateLock("clerkHelpBttn", 21);

	/* CHECKOUT SHARED DATA MONITOR */
	customersAtCheckout = CreateIntMV("cstmrsAtChckt MV" , charCount("cstmrsAtChckt MV\n")-1, MAXCLERKS);
	refundingTransaction = CreateIntMV("rfndngTrnsctn MV" , charCount("rfndngTrnsctn MV\n")-1, MAXCLERKS);
	paymentDue = CreateIntMV("paymentDue MV" , charCount("paymentDue MV\n")-1, MAXCLERKS);
	amountPaid = CreateIntMV("amountPaid MV" , charCount("amountPaid MV\n")-1, MAXCLERKS);
	lastItemGiven = CreateIntMV("lastItemGiven MV" , charCount("lastItemGiven MV\n")-1, MAXCLERKS);
	itemToScan = CreateIntMV("itemToScan MV" , charCount("itemToScan MV\n")-1, MAXCLERKS);
	customersAtCheckoutLocks[0] = CreateLock("cstmrsAtChcktLck0", charCount("cstmrsAtChcktLck0\n")-1);
	customersAtCheckoutLocks[1] = CreateLock("cstmrsAtChcktLck1", charCount("cstmrsAtChcktLck1\n")-1);
	customersAtCheckoutLocks[2] = CreateLock("cstmrsAtChcktLck2", charCount("cstmrsAtChcktLck2\n")-1);
	customersAtCheckoutLocks[3] = CreateLock("cstmrsAtChcktLck3", charCount("cstmrsAtChcktLck3\n")-1);
	customersAtCheckoutLocks[4] = CreateLock("cstmrsAtChcktLck4", charCount("cstmrsAtChcktLck4\n")-1);

	/* CASH REGISTER MONITOR */
	cashRegisterFinishedStatus = CreateIntMV("cshRgstrFnshdStts MV" , charCount("cshRgstrFnshdStts MV\n")-1, MAXCLERKS);
	cashRegisterSales = CreateIntMV("cashRegisterSales MV" , charCount("cashRegisterSales MV\n")-1, MAXCLERKS);
	cashRegisterAmount = CreateIntMV("cshRgstrAmnt MV" , charCount("cshRgstrAmnt MV\n")-1, MAXCLERKS);
	cashRegisterLocks[0] = CreateLock("registerLock0", 13);
	cashRegisterLocks[1] = CreateLock("registerLock1", 13);
	cashRegisterLocks[2] = CreateLock("registerLock2", 13);
	cashRegisterLocks[3] = CreateLock("registerLock3", 13);
	cashRegisterLocks[4] = CreateLock("registerLock4", 13);
	cashRegisterCV[0] = CreateCV("registerCV0", 11);
	cashRegisterCV[1] = CreateCV("registerCV1", 11);
	cashRegisterCV[2] = CreateCV("registerCV2", 11);
	cashRegisterCV[3] = CreateCV("registerCV3", 11);
	cashRegisterCV[4] = CreateCV("registerCV4", 11);

	/* CASH REGISTER ASSISTANCE MONITOR */
	cashRegisterAssistance = CreateIntMV("cshRgstrAssstnc MV" , charCount("cshRgstrAssstnc MV\n")-1, MAXCLERKS);
	cashRegisterRequest = CreateIntMV("cshRgstrRqst MV" , charCount("cshRgstrRqst MV\n")-1, MAXCLERKS);
	cashRegisterAssistanceLock = CreateLock("cashRegAssistLock", charCount("cashRegAssistLock\n")-1); 
	cashRegisterAssistanceCV[0] = CreateCV("cashReg0AssistCV", charCount("cashReg_AssistCV\n")-1);
	cashRegisterAssistanceCV[1] = CreateCV("cashReg1AssistCV", charCount("cashReg_AssistCV\n")-1);
	cashRegisterAssistanceCV[2] = CreateCV("cashReg2AssistCV", charCount("cashReg_AssistCV\n")-1);
	cashRegisterAssistanceCV[3] = CreateCV("cashReg3AssistCV", charCount("cashReg_AssistCV\n")-1);
	cashRegisterAssistanceCV[4] = CreateCV("cashReg4AssistCV", charCount("cashReg_AssistCV\n")-1);
	
	/* STOCKBOY STATUS MONITOR */
	stockboyStatus = CreateIntMV("stockboyStatus MV" , charCount("stockboyStatus MV\n")-1, MAXSTOCKBOYS);
	stockboyStatusLock = CreateLock("stockboyStatusLock", charCount("stockboyStatusLock\n")-1);
	
	/* STOCKBOY MONITOR */
	stockboyStates =  CreateIntMV("stockboyStates MV" , charCount("stockboyStates MV\n")-1, MAXSTOCKBOYS);
	stockboyLocks[0] = CreateLock("stockboy0lock", charCount("stockboy0lock\n")-1);
	stockboyLocks[1] = CreateLock("stockboy1lock", charCount("stockboy1lock\n")-1);
	stockboyLocks[2] = CreateLock("stockboy2lock", charCount("stockboy2lock\n")-1);
	stockboyLocks[3] = CreateLock("stockboy3lock", charCount("stockboy3lock\n")-1);
	stockboyLocks[4] = CreateLock("stockboy4lock", charCount("stockboy4lock\n")-1);
	stockboyLocks[5] = CreateLock("stockboy5lock", charCount("stockboy5lock\n")-1);
	stockboyLocks[6] = CreateLock("stockboy6lock", charCount("stockboy6lock\n")-1);
	stockboyLocks[7] = CreateLock("stockboy7lock", charCount("stockboy7lock\n")-1);
	stockboyLocks[8] = CreateLock("stockboy8lock", charCount("stockboy8lock\n")-1);
	stockboyLocks[9] = CreateLock("stockboy9lock", charCount("stockboy9lock\n")-1);
	stockboysCV[0] = CreateCV("stockboy0CV", charCount("stockboy0CV\n")-1);
	stockboysCV[1] = CreateCV("stockboy1CV", charCount("stockboy1CV\n")-1);
	stockboysCV[2] = CreateCV("stockboy2CV", charCount("stockboy2CV\n")-1);
	stockboysCV[3] = CreateCV("stockboy3CV", charCount("stockboy3CV\n")-1);
	stockboysCV[4] = CreateCV("stockboy4CV", charCount("stockboy4CV\n")-1);
	stockboysCV[5] = CreateCV("stockboy5CV", charCount("stockboy5CV\n")-1);
	stockboysCV[6] = CreateCV("stockboy6CV", charCount("stockboy6CV\n")-1);
	stockboysCV[7] = CreateCV("stockboy7CV", charCount("stockboy7CV\n")-1);
	stockboysCV[8] = CreateCV("stockboy8CV", charCount("stockboy8CV\n")-1);
	stockboysCV[9] = CreateCV("stockboy9CV", charCount("stockboy9CV\n")-1);
	
	/* CUSTOMER-STOCKBOY SHARED DATA MONITOR*/
	customerWithStockboy = CreateIntMV("cstmrWithSB_MV" , charCount("cstmrWithSB_MV\n")-1, MAXSTOCKBOYS);
	stockboyItemToHelp = CreateIntMV("sbItemToHelpMV" , charCount("sbItemToHelpMV\n")-1, MAXSTOCKBOYS);
	shelfNumberForCustomer = CreateIntMV("shelfNum4CstmrMV" , charCount("shelfNum4CstmrMV\n")-1, MAXSTOCKBOYS);
	aisleNumberForCustomer = CreateIntMV("aisleNum4CstmrMV" , charCount("aisleNum4CstmrMV\n")-1, MAXSTOCKBOYS);
	customerWithStockboyLock[0] = CreateLock("cstmrWithSB0Lock", charCount("cstmrWithSB0Lock\n")-1);
	customerWithStockboyLock[1] = CreateLock("cstmrWithSB1Lock", charCount("cstmrWithSB1Lock\n")-1);
	customerWithStockboyLock[2] = CreateLock("cstmrWithSB2Lock", charCount("cstmrWithSB2Lock\n")-1);
	customerWithStockboyLock[3] = CreateLock("cstmrWithSB3Lock", charCount("cstmrWithSB3Lock\n")-1);
	customerWithStockboyLock[4] = CreateLock("cstmrWithSB4Lock", charCount("cstmrWithSB4Lock\n")-1);
	customerWithStockboyLock[5] = CreateLock("cstmrWithSB5Lock", charCount("cstmrWithSB5Lock\n")-1);
	customerWithStockboyLock[6] = CreateLock("cstmrWithSB6Lock", charCount("cstmrWithSB6Lock\n")-1);
	customerWithStockboyLock[7] = CreateLock("cstmrWithSB7Lock", charCount("cstmrWithSB7Lock\n")-1);
	customerWithStockboyLock[8] = CreateLock("cstmrWithSB8Lock", charCount("cstmrWithSB8Lock\n")-1);
	customerWithStockboyLock[9] = CreateLock("cstmrWithSB9Lock", charCount("cstmrWithSB9Lock\n")-1);
	
	/* AISLE HELP MONITOR */
	aisleHelpLine = CreateIntMV("aisleHelpLine MV" , charCount("aisleHelpLine MV\n")-1, MAXAISLES);
	stockboyReadyLock[0] = CreateLock("sbReady0Lock", charCount("sbReady0Lock\n")-1);
	stockboyReadyLock[1] = CreateLock("sbReady1Lock", charCount("sbReady1Lock\n")-1);
	stockboyReadyLock[2] = CreateLock("sbReady2Lock", charCount("sbReady2Lock\n")-1);
	stockboyReadyLock[3] = CreateLock("sbReady3Lock", charCount("sbReady3Lock\n")-1);
	stockboyReadyLock[4] = CreateLock("sbReady4Lock", charCount("sbReady4Lock\n")-1);
	aisleHelpLocks[0] = CreateLock("aisle0Lock", 10);
	aisleHelpLocks[1] = CreateLock("aisle1Lock", 10);
	aisleHelpLocks[2] = CreateLock("aisle2Lock", 10);
	aisleHelpLocks[3] = CreateLock("aisle3Lock", 10);
	aisleHelpLocks[4] = CreateLock("aisle4Lock", 10);
	aisleCV[0] = CreateCV("aisle0CV", 8);
	aisleCV[1] = CreateCV("aisle1CV", 8);
	aisleCV[2] = CreateCV("aisle2CV", 8);
	aisleCV[3] = CreateCV("aisle3CV", 8);
	aisleCV[4] = CreateCV("aisle4CV", 8);
	
	/* CUSTOMER ASSISTANCE MONITRO */
	customerAssistanceButton = CreateIntMV("cstmrAssistBttnMV" , charCount("cstmrAssistBttnMV\n")-1, 1);
	customerAssistanceButtonLock = CreateLock("cstmrAssistBttnLock", charCount("cstmrAssistBttnLock\n")-1);
	customerAssistanceButtonCV = CreateCV("cstmrAssistBttnCV", charCount("cstmrAssistBttnLock\n")-1);
	
	/* STOCK ROOM LINE MONITOR */
	stockroomLineLength =  CreateIntMV("stkroomLineMV" , charCount("stkroomLineMV\n")-1, 1);
	stockboyInStockroom = CreateIntMV("sbInStockroomMV" , charCount("sbInStockroomMV\n")-1, 1);
	stockroomLineLock = CreateLock("stkroomLineLock", charCount("stockroomLineLock\n")-1);
	stockroomLineCV = CreateCV("stockroomCV", charCount("stkroomCV")-1);
	
	/* STOCK ROOM MONITOR */
	stockroomLock = CreateLock("stockroomLock", charCount("stockroomLock\n")-1);
	
	/* SHELF MONITOR */
	shelfState = CreateIntMV("shelfStateMV" , charCount("shelfStateMV\n")-1, MAXSHELVES);
	shelfAisle = CreateIntMV("shelfAisleMV" , charCount("shelfAisleMV\n")-1, MAXSHELVES);
	shelfItem = CreateIntMV("shelfItemMV" , charCount("shelfItemMV\n")-1, MAXSHELVES);
	shelfQuantity = CreateIntMV("shelfQuantityMV" , charCount("shelfQuantityMV\n")-1, MAXSHELVES);
	shelfLocks[0] = CreateLock("shelfLock0", 10);
	shelfLocks[1] = CreateLock("shelfLock1", 10);
	shelfLocks[2] = CreateLock("shelfLock2", 10);
	shelfLocks[3] = CreateLock("shelfLock3", 10);
	shelfLocks[4] = CreateLock("shelfLock4", 10);
	shelfLocks[5] = CreateLock("shelfLock5", 10);
	shelfLocks[6] = CreateLock("shelfLock6", 10);
	shelfLocks[7] = CreateLock("shelfLock7", 10);
	shelfLocks[8] = CreateLock("shelfLock8", 10);
	shelfLocks[9] = CreateLock("shelfLock9", 10);
}
	
	
	
void Initialize() {
	int j, m;
	
	AlterIntMV(customerCount, 0, 0);
	AlterIntMV(clerkCount, 0, 0);
	AlterIntMV(cashRegisterCount, 0, 0);
	AlterIntMV(stockboyCount, 0, 0);

	AlterIntMV(totalCustomersServed, 0, 0); 
	AlterIntMV(storeTotal, 0, 0);
	
	AlterIntMV(checkoutAssistance, 0, 0);
	AlterIntMV(clerkAssistanceButton, 0, 0);
	AlterIntMV(cashRegisterFinishedStatus, 0, 0);
	AlterIntMV(customerAssistanceButton, 0, 0);
	
	AlterIntMV(stockroomLineLength, 0, 0);
	AlterIntMV(stockboyInStockroom, 0, -1);
	
	for(j=0; j<MAXCLERKS; j++) {
		AlterIntMV(clerkLineLengths, j, 0);
		AlterIntMV(clerkStates, j, 1);
		AlterIntMV(paymentDue, j, 0);
		AlterIntMV(lastItemGiven, j, 0);
		AlterIntMV(itemToScan, j, -1);
		AlterIntMV(cashRegisterFinishedStatus, j, 0);
		AlterIntMV(customersAtCheckout, j, -1);
		AlterIntMV(cashRegisterSales, j, 0);
		AlterIntMV(cashRegisterAmount, j, 0);
		AlterIntMV(cashRegisterAssistance, j, 0);
		AlterIntMV(cashRegisterRequest, j, 0);
		AlterIntMV(refundingTransaction, j, 0);
	}
	
	m=0;
	for(j=0; j < MAXSHELVES; j++) {
		AlterIntMV(shelfState , j, 0);
		AlterIntMV(shelfAisle, j, m);
		AlterIntMV(shelfItem, j, j);
		AlterIntMV(shelfQuantity, j, MAXSHELFQUANTITY);
	
		if(j == 1 || j == 3 || j == 5 || j == 7)
			m++;
	}
	
	for(j=0; j < MAXSTOCKBOYS; j++) {
		AlterIntMV(stockboyStatus, j, -1);
		AlterIntMV(stockboyStates, j, 1);
		AlterIntMV(stockboyItemToHelp, j, -1);
		AlterIntMV(shelfNumberForCustomer, j, -1);
		AlterIntMV(aisleNumberForCustomer, j, -1);
		AlterIntMV(customerWithStockboy, j, -1);
		AlterIntMV(stockboyItemToHelp, j, -1);
	}
	
	for(j=0; j < MAXAISLES; j++) {
		AlterIntMV(aisleHelpLine, j, 0);
	}
	
}

int Customer_findShortestLine(int initialCall) {
	int k;
	int shortestLineIndex = -1;
	int shortestLineLength = 999;
	/* find shortest line */
	LockAcquire(clerkLineLock);
	
	for(k = 0 ; k < MAXCLERKS ; k++)
	{
		/* clerkState of 1 means on break, 0 means currently working */
		if(RetrieveIntMV(clerkStates, k) == 1)
		{
			continue;
		}
		if(RetrieveIntMV(clerkLineLengths, k) < shortestLineLength)
		{
			shortestLineLength = RetrieveIntMV(clerkLineLengths, k);
			shortestLineIndex = k;
		}
	}

	/* if all are clerks on break */
	if(shortestLineIndex == -1)
	{
		
		LockRelease(clerkLineLock);
		LockAcquire(checkoutAssistanceLock);
		
		Write("All clerks on break.\n", charCount("All clerks on break.\n"), ConsoleOutput);
		if(RetrieveIntMV(checkoutAssistance, 0) && initialCall == 1)
		{
			AlterIntMV(checkoutAssistance, 0, 1);
			CVWait(checkoutAssistanceCV, checkoutAssistanceLock);
			Yield();
		}
		else 
		{
			CVWait(checkoutAssistanceCV, checkoutAssistanceLock);
			Yield();
		}
		LockRelease(checkoutAssistanceLock);
		shortestLineIndex = Customer_findShortestLine(0);
	}
	return shortestLineIndex;
}

int Customer_findStockboy(int id, int initialCall) {
	int i, j;
	int stockboyAisle;
	
	LockAcquire(stockboyStatusLock); 
	stockboyAisle = -1; /* reset our stockboy aisle variable */
	for( i = 0 ; i < MAXSTOCKBOYS ; i++) /* search all stockboys */
	{
		for(j = 0; j < MAXAISLES ; j++) /* and see their current location */
		{
			if(RetrieveIntMV(stockboyStatus, i) == j) /* if they are currently in an aisle */
			{
				stockboyAisle = j; /* save it in our stockboy aisle variable */
				SWrite("Customer [%d] is looking for assistance in Aisle [%d].\n",
						charCount("Customer [%d] is looking for assistance in Aisle [%d].\n"), id, stockboyAisle);
				break; /* break out of inner for loop */
			}
		}
		if(stockboyAisle != -1) /* if a stockboy is already found then stop searching */
		{
			break; /* get out of outer for loop */
		}
	}
	
	/* if a stockboy can't be found in any aisle, press the customerAssistanceButton */
	if(stockboyAisle == -1) { 
		SWrite("Customer [%d] can't find a stockboy at all.\n",
			charCount("Customer [%d] can't find a stockboy at all.\n"), id, 0);
	
		LockAcquire(customerAssistanceButtonLock);
		LockRelease(stockboyStatusLock);

		if(RetrieveIntMV(customerAssistanceButton, 0) == 0 && initialCall == 1) {
			/* Press Assistance Button */
			AlterIntMV(customerAssistanceButton, 0, 1);
			CVWait(customerAssistanceButtonCV, customerAssistanceButtonLock);
		}
		else {
			/*some customer already pressed it */
			CVWait(customerAssistanceButtonCV, customerAssistanceButtonLock);
		}
		
		LockRelease(customerAssistanceButtonLock);
		stockboyAisle = Customer_findStockboy(id, 0);
	}
	return stockboyAisle;
}

/* MUST ACQUIRE stockboyStatusLock AND aisleHelpLock before this method */
/* establishes the connection between a customer and a stockboy that's ready to help */
int Customer_approachStockboy(int id, int myAisle, int itemNeeded) {
	int i;
	int myStockboy = -1;
	
	LockAcquire(stockboyStatusLock);
	/* search array for a stockboy that's available (he is stockboyHelpAisle + 10) */
	for(i = 0 ; i < MAXSTOCKBOYS ; i++) {
		if(RetrieveIntMV(stockboyStatus, i) == myAisle + 10) {
			myStockboy = i;
			break;
		}
	}

	if(myStockboy == -1) {
		SWrite("FATAL ERROR: Customer[%d] called out of aisle[%d] help line but cannot find an available StockBoy.\n",
			charCount("FATAL ERROR: Customer[%d] called out of aisle[%d] help line but cannot find an available StockBoy.\n"), id, myAisle);
		Halt();
	}
	
	/* accept this stockboy's help by giving him a normal status */
	AlterIntMV(stockboyStatus, myStockboy, RetrieveIntMV(stockboyStatus, myStockboy) - 10);
	LockRelease(stockboyStatusLock);
	/* set data to be shared with the stockboy */
	LockAcquire(customerWithStockboyLock[myStockboy]);
	AlterIntMV(customerWithStockboy, myStockboy, id);
	AlterIntMV(stockboyItemToHelp, myStockboy, itemNeeded);
	LockRelease(customerWithStockboyLock[myStockboy]);
	return  myStockboy;
}

int Customer_askStockboy(id, myStockboy) {
	int i;
	int sizes[2];
	int inputs[3];
	int shelfNumber = -1;
	int aisleNumber = -1;
	LockAcquire(stockboyLocks[myStockboy]);
	
	SWrite("Customer [%d] is asking for assistance from StockBoy [%d].\n", 
		charCount("Customer [%d] is asking for assistance from StockBoy [%d].\n"), id, myStockboy);
	CVSignal(stockboysCV[myStockboy], stockboyLocks[myStockboy]);
	CVWait(stockboysCV[myStockboy], stockboyLocks[myStockboy]);
	
	LockAcquire(customerWithStockboyLock[myStockboy]);
	shelfNumber = RetrieveIntMV(shelfNumberForCustomer, myStockboy);
	aisleNumber = RetrieveIntMV(aisleNumberForCustomer, myStockboy);
	LockRelease(customerWithStockboyLock[myStockboy]);
	
	sizes[0] = charCount("Customer [%d] got aisle [%d] from StockBoy [%d].\n");
	sizes[1] = 3;
	
	inputs[0] = id;
	inputs[1] = aisleNumber;
	inputs[2] = myStockboy;
	
	WriteAll("Customer [%d] got aisle [%d] from StockBoy [%d].\n", sizes, inputs);
	CVSignal(stockboysCV[myStockboy], stockboyLocks[myStockboy]);
	
	LockRelease(stockboyLocks[myStockboy]);

	return shelfNumber;
}

void Customer_cantFindItem(int id, int item, int *cart, int cartIndex) {
	int i, j, k;
	int helpAisle = -1;
	int myStockboy = -1;
	int shelfToFindItem = -1;
	
	SWrite("Customer [%d] is not able to find item [%d] and is searching for StockBoy.\n",
		charCount("Customer [%s] is not able to find item [%d] and is searching for StockBoy.\n"), 
		id, item);
		
	helpAisle = Customer_findStockboy(id, 1);
	
	/* - we have found a stockboy
	*  - we still have the stockboyStatusLock
	*  - we know our stockboy's aisle number */
	LockRelease(stockboyStatusLock);
	LockAcquire(aisleHelpLocks[helpAisle]);
	
	/* get in the help line of the aisle that we found a stockboy in */
	AlterIntMV(aisleHelpLine, helpAisle, RetrieveIntMV(aisleHelpLocks, helpAisle)+1);
	SWrite("Customer [%d] Lining up for help in aisle [%d].\n", 
		charCount("Customer [%d] Lining up for help in aisle [%d].\n"), id, helpAisle);
	CVWait(aisleCV[helpAisle], RetrieveIntMV(aisleHelpLocks, helpAisle));

	/* get out of help line */
	AlterIntMV(aisleHelpLine, helpAisle, RetrieveIntMV(aisleHelpLocks, helpAisle)-1);
	
	/* secure the attention of a stockboy */
	myStockboy = Customer_approachStockboy(id, helpAisle, item);

	LockAcquire(stockboyLocks[myStockboy]);
	
	CVSignal(stockboysCV[myStockboy], stockboyLocks[myStockboy]);
	LockRelease(aisleHelpLocks[helpAisle]);
	CVWait(stockboysCV[myStockboy], stockboyLocks[myStockboy]);
	
	
	shelfToFindItem = Customer_askStockboy(id, myStockboy);

	if(shelfToFindItem == -1) {
		SWrite("Customer [%d] recieved invalid shelf [%d].\n",
			charCount("Customer [%d] recieved invalid shelf [%d].\n"),
			id, shelfToFindItem);
		Customer_cantFindItem(id, item, (int *)cart, cartIndex);
		return;
	}	
	
	/* customer "looking" for the item */
	/* also allows time in case that particular shelf is empty and is being restocked */
	for(i=0; i<20; i++) {
		Yield();
	}
									
	/* customer gets item from the shelf */
	LockAcquire(shelfLocks[shelfToFindItem]);
	/* take an item and put it in the cart */
	if(RetrieveIntMV(shelfQuantity, shelfToFindItem) > 0) {
		AlterIntMV(shelfQuantity, shelfToFindItem, RetrieveIntMV(shelfQuantity, shelfToFindItem)-1);
		cart[cartIndex] = RetrieveIntMV(shelfItem, shelfToFindItem);
		SWrite("Customer [%d] added item [%d] to shopping cart.\n",
			charCount("Customer [%d] added item [%d] to shopping cart.\n"),
			id, cart[cartIndex]);
		LockRelease(shelfLocks[shelfToFindItem]);
	}
	else {
		/* shelf is empty */
		SWrite("Customer [%d] sees shelf [%d] is empty.\n",
			charCount("Customer [%d] sees shelf [%d] is empty.\n"),
			id, shelfToFindItem);
		/* Stock Boy Assistnace Again */
		LockRelease(shelfLocks[shelfToFindItem]);
		LockAcquire(customerAssistanceButtonLock);

		if(RetrieveIntMV(customerAssistanceButton, 0) == 0) {
			/* Press Assistance Button */
			AlterIntMV(customerAssistanceButton, 0, 1);
			CVWait(customerAssistanceButtonCV, customerAssistanceButtonLock);
		}
		else {
			/*some customer already pressed it */
			CVWait(customerAssistanceButtonCV, customerAssistanceButtonLock);
		}
		
		LockRelease(customerAssistanceButtonLock);
		LockAcquire(shelfLocks[shelfToFindItem]);
		while(RetrieveIntMV(shelfQuantity, shelfToFindItem) == 0) {
			LockRelease(shelfLocks[shelfToFindItem]);
			for(i=0; i<10; i++) {
				Yield();
			}
			LockAcquire(shelfLocks[shelfToFindItem]);
		}
		AlterIntMV(shelfQuantity, shelfToFindItem, RetrieveIntMV(shelfQuantity, shelfToFindItem)-1);
		cart[cartIndex] = RetrieveIntMV(shelfItem, shelfToFindItem);
		SWrite("Customer [%d] added item [%d] to shopping cart.\n",
			charCount("Customer [%d] added item [%d] to shopping cart.\n"),
			id, cart[cartIndex]);
		LockRelease(shelfLocks[shelfToFindItem]);
	}
}

void Customer() {
	
	int i, j, k,m;
	int randomness;
	int returning;
	int id;
	int shortestLineIndex = 0;
	int shortestLineLength = 0;
	int shoppingCart[MAXCARTSIZE];
	int shoppingCartSize = 0;
	int shoppingList[MAXCARTSIZE];
	int shoppingListSize = 0;
	int stockboyHelpAisle = -1;
	int myStockboy = -1;
	int itemTypeNeeded = -1;
	int aisleToGoTo = -1;
	int sizes[2];
	int inputs[3];
	
	Write("Starting Customer\n", 18, ConsoleOutput);
	LockAcquire(globalLock);
	id = RetrieveIntMV(customerCount, 0);
	AlterIntMV(customerCount, 0, RetrieveIntMV(customerCount, 0)+1);
	/*SWrite("[%d] customers in store.\n", 25, customerCount, 0);*/
	LockRelease(globalLock);
	
	sizes[1] = 3;
	inputs[0] = id;
	
	/* 25% of the time, the Customer will be returning items instead of shopping */
	k = Rand(0,4);
	if(k == 0) {
		returning = 1;
	} else {
		returning = 0;
	}
	
	if(returning == 0) {
		/*  Fill Shopping list */
		/* Randomly generate an int from 1 to 10 */
		shoppingListSize = Rand(1,10);
		/* For each item, randomly generate a productID */
		for(k = 0 ; k < shoppingListSize ; k++) {
			/* Randomly generate an int from 0 to 9 */
			shoppingList[k] = Rand(0,(int)MAXSHELVES-1);
		}
		SWrite("Customer [%d] wants to buy [%d] no.of items.\n", charCount("Customer [%d] wants to buy [%d] no.of items.\n"),
			id, shoppingListSize);

		/* Go find the items on the shopping list */
		for(k = 0 ; k < shoppingListSize ; k++)
		{	
			SWrite("Customer [%d] looking for item [%d].\n",
				charCount("Customer [%d] looking for item [%d].\n"), id, shoppingList[k]);
				
			randomness = Rand(1,4);
			if(randomness != 0 )
			{
				/* 75% of the time, the customer CAN find what he is looking for*/
			
				LockAcquire(shelfLocks[shoppingList[k]]);
				/* take an item and put it in my cart */
				if(RetrieveIntMV(shelfQuantity, k) > 0)
				{
					AlterIntMV(shelfQuantity, shoppingList[k], RetrieveIntMV(shelfQuantity, k)-1);
					shoppingCart[k] = RetrieveIntMV(shelfItem, shoppingList[k]);
					SWrite("Customer [%d] added item [%d] to shopping cart.\n",
							charCount("Customer [%d] added item [%d] to shopping cart.\n"),
							id, shoppingCart[k]);
					LockRelease(shelfLocks[shoppingList[k]]);
				}
				else
				{
					/* shelf is empty */
					/* Stock Boy Assistnace */
					itemTypeNeeded = RetrieveIntMV(shelfItem, shoppingList[k]); /* save which item we need to ask the stockboy about*/
					LockRelease(shelfLocks[shoppingList[k]]);
					
					Customer_cantFindItem(id, itemTypeNeeded, shoppingCart, k);
				}
			}
			else /* cant-find-an-item scenario */
			{
				LockAcquire(shelfLocks[shoppingList[k]]);
				itemTypeNeeded = RetrieveIntMV(shelfItem, shoppingList[k]); /* save which item we need to ask the stockboy about*/
				LockRelease(shelfLocks[shoppingList[k]]);
				
				Customer_cantFindItem(id, itemTypeNeeded, shoppingCart, k);
			}/*end for loop for finding items*/
			shoppingCartSize++;
		} /* end for */
	}
	else {
	/* Customer is a returning customer */
		/* Randomly Fill Shopping Cart */
		shoppingCartSize = Rand(1,10);
		
		for(k = 0 ; k < shoppingCartSize ; k++)
		{
			/* Randomly generate an int from 0 to 9 */
			shoppingCart[k] = Rand(0,(int)MAXSHELVES-1);
		}
		SWrite("Customer [%d] needs to return [%d] no.of items.\n",
			charCount("Customer [%d] needs to return [%d] no.of items.\n"), id, shoppingCartSize);
	}
	
	shortestLineIndex = Customer_findShortestLine(1);
	
	AlterIntMV(clerkLineLengths, shortestLineIndex, RetrieveIntMV(clerkLineLengths, shortestLineIndex)+1);
	
	sizes[0] = charCount("Customer [%d] chose Clerk [%d] with line length of [%d].\n");
	inputs[1] = shortestLineIndex;
	inputs[2] = RetrieveIntMV(clerkLineLengths, shortestLineIndex)-1;
	WriteAll("Customer [%d] chose Clerk [%d] with line length of [%d].\n", sizes, inputs);

	CVWait(clerkLineCV[shortestLineIndex], clerkLineLock);
	AlterIntMV(clerkLineLengths, shortestLineIndex, RetrieveIntMV(clerkLineLengths, shortestLineIndex)-1);
	LockAcquire(clerkLocks[shortestLineIndex]);
	LockRelease(clerkLineLock);
	
	SWrite("Customer [%d] approaching clerk[%d].\n", charCount("Customer [%d] approaching clerk[%d].\n"),
			id, shortestLineIndex);
	
	AlterIntMV(refundingTransaction, shortestLineIndex, returning);
	AlterIntMV(customersAtCheckout, shortestLineIndex, id);
	CVSignal(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
	CVWait(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);

	for(k = 0 ; k < shoppingCartSize ; k++)
	{
		AlterIntMV(itemToScan, shortestLineIndex, shoppingCart[k]);

		sizes[0] = charCount("Customer [%d] gives item [%d] to Clerk [%d].\n");
		inputs[1] = RetrieveIntMV(itemToScan,shortestLineIndex);
		inputs[2] = shortestLineIndex;
		WriteAll("Customer [%d] gives item [%d] to Clerk [%d].\n", sizes, inputs);
				
		if(k == shoppingCartSize - 1) 
		{
			AlterIntMV(lastItemGiven, shortestLineIndex, 1);
			sizes[0] = charCount("Customer [%d] tells Clerk [%d], I have given you all my [%d] items.\n");
			inputs[1] = shortestLineIndex;
			inputs[2] = shoppingCartSize;
			WriteAll("Customer [%d] tells Clerk [%d], I have given you all my [%d] items.\n", sizes, inputs);
			
		}
		CVSignal(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
		CVWait(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
	}

	AlterIntMV(lastItemGiven, shortestLineIndex, 0);
	
	if(returning == 0) {
		AlterIntMV(amountPaid, shortestLineIndex, RetrieveIntMV(paymentDue,shortestLineIndex));
		
		sizes[0] = charCount("Customer [%d] pays [%d] to Clerk [%d] and is now waiting for receipt.\n");
		inputs[1] = RetrieveIntMV(amountPaid,shortestLineIndex);
		inputs[2] = shortestLineIndex;
		WriteAll("Customer [%d] pays [%d] to Clerk [%d] and is now waiting for receipt.\n", sizes, inputs);
	}
	else {
		sizes[0] = charCount("Customer [%d] receives [%d] from Clerk [%d] and is now waiting for receipt.\n");
		inputs[1] = RetrieveIntMV(paymentDue,shortestLineIndex);
		inputs[2] = shortestLineIndex;
		WriteAll("Customer [%d] receives [%d] from Clerk [%d] and is now waiting for receipt.\n", sizes, inputs);
	}
	
	CVSignal(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
	CVWait(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
		
	SWrite("Customer [%d] got receipt from Clerk [%d] and is now leaving.\n", 
		charCount("Customer [%d] got receipt from Clerk [%d] and is now leaving.\n"), id, shortestLineIndex);
	CVSignal(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
	LockRelease(clerkLocks[shortestLineIndex]);
	
	Exit(-1);
}

int main() {
	int i;
	
	CreateMonitorsLocksCVs();
	Initialize();
	
	for(i = 0 ; i < 10 ; i++) {
		Fork(&Customer, "Customer", 8);
	}
}